#include "mmo.h"
#include "assert.h"

/**
 * set the memeory region with `mx_uint8_t` data
 * @param data: the target data
 * @param paddr: the start address of `mx_uint8_t` array
 * @param len: the length of `mx_uint8_t` array
 * @return: void
 */
void mmo_set8 (mx_uint8_t data, mx_uint8_pt paddr, mx_usize_t len)
{
    mx_uint8_pt end = paddr + len;
    /* front-not-aligned data fill with byte */
    while ((mx_usize_t)paddr & MX_CPU_BITWIDE_MASK)
    {
        if (paddr == end)
            return;
        *paddr++ = data;
    }
    /* expand data to `mx_usize_t`, data * 0x01010101... */
    mx_usize_t exd = data * (~(mx_usize_t)0 / 0xff);
    /* get the end-cpu-aligned address */
    mx_uint8_pt alignend = (mx_uint8_pt)(((mx_usize_t)end) & ~MX_CPU_BITWIDE_MASK);
    /* aligned data fill with `mx_usize_t` */
    while (paddr < alignend)
    {
        *((mx_usize_pt)paddr) = exd;
        paddr += MX_CPU_BITWIDE_BYTE;
    }
    /* tail-not-aligned data fill with byte */
    while (paddr < end)
        *paddr++ = data;
}

/**
 * set the memeory region with `mx_uint16_t` data
 * @param data: the target data
 * @param paddr: the start address of `mx_uint16_t` array, must be 2-byte aligned
 * @param len: the length of `mx_uint16_t` array
 * @return: void
 */
void mmo_set16 (mx_uint16_t data, mx_uint16_pt paddr, mx_usize_t len)
{
    /* alignment checkout */
    assert((mx_usize_t)paddr & 1);
    mx_uint16_pt end = paddr + len;
    /* front-not-aligned data fill with half-word */
    while ((mx_usize_t)paddr & MX_CPU_BITWIDE_MASK)
    {
        if (paddr == end)
            return;
        *paddr++ = data;
    }
    /* expand data to `mx_usize_t`, data * 0x01010101... */
    mx_usize_t exd = data * (~(mx_usize_t)0 / 0xffff);
    /* get the end-cpu-aligned address */
    mx_uint16_pt alignend = (mx_uint16_pt)(((mx_usize_t)end) & ~MX_CPU_BITWIDE_MASK);
    /* aligned data fill with `mx_usize_t` */
    while (paddr < alignend)
    {
        *((mx_usize_pt)paddr) = exd;
        paddr = (mx_uint16_pt)((mx_usize_t)paddr + MX_CPU_BITWIDE_BYTE);
    }
    /* tail-not-aligned data fill with byte */
    while (paddr < end)
        *paddr++ = data;
}

/**
 * copy a memory region to another
 * @param dst: the start address of destination memory region
 * @param src: the start address of srouce memory region
 * @param size: the size of memeory region
 * @return: void
 */
void mmo_copy (mx_uint8_pt dst, mx_uint8_pt src, mx_usize_t size)
{
    mx_uint8_pt end = src + size;
    /* front-not-aligned data copy by byte */
    while ((mx_usize_t)src & MX_CPU_BITWIDE_MASK)
    {
        if (src == end)
            return;
        *dst++ = *src++;
    }
    /* get the end-cpu-aligned address */
    mx_uint8_pt alignend = (mx_uint8_pt)(((mx_usize_t)end) & ~MX_CPU_BITWIDE_MASK);
    /* aligned data copy by `mx_usize_t` */
    while (src < alignend)
    {
        *((mx_usize_pt)dst) = *((mx_usize_pt)src);
        dst += MX_CPU_BITWIDE_BYTE;
        src += MX_CPU_BITWIDE_BYTE;
    }
    /* tail-not-aligned data copy by byte */
    while (src < end)
        *dst++ = *src++;
}
